package log

import (
	"github.com/lestrrat/go-file-rotatelogs"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"os"
	"time"
)

const DefaultLogPath = "./test" // 默认输出日志文件路径
const (
	TIME_LAYOUT = "2006-01-02 15:04:05"
)

var logger *zap.Logger
var sugaredLogger *zap.SugaredLogger

/**
 * @brief: 	logger默认配置
 * @author: lianpu.sun
 */
func InitDefaultLogger() error {
	config := LogConfigs{
		LogLevel:          "debug",    // 输出日志级别 "debug" "info" "warn" "error"
		LogFormat:         "logfmt",   // 输出日志格式 logfmt, json
		LogPath:           "./log/",   // 输出日志文件位置
		LogFileName:       "test.log", // 输出日志文件名称
		LogSplitTime:      time.Hour * 24,
		LogFileMaxBackups: 10,                  // 输出最大日志备份个数
		LogMaxAge:         time.Hour * 24 * 30, // 日志保留时间，单位: 天 (day)
		LogStdout:         true,                // 是否输出到控制台
	}
	return InitLogger(config)
}

/**
 * @brief: 	初始化logger配置
 * @author: lianpu.sun
 */
func InitLogger(conf LogConfigs) error {
	logLevel := map[string]zapcore.Level{
		"debug": zapcore.DebugLevel,
		"info":  zapcore.InfoLevel,
		"warn":  zapcore.WarnLevel,
		"error": zapcore.ErrorLevel,
	}
	writeSyncer, err := getLogWriter(conf) // 日志文件配置 文件位置和切割
	if err != nil {
		return err
	}
	encoder := getEncoder(conf)          // 获取日志输出编码
	level, ok := logLevel[conf.LogLevel] // 日志打印级别
	if !ok {
		level = logLevel["info"]
	}
	core := zapcore.NewCore(encoder, writeSyncer, level)
	logger = zap.New(core, zap.AddCaller())
	sugaredLogger = logger.Sugar()
	return nil
}

/**
 * @brief: 	编码器(如何写入日志)
 * @author: lianpu.sun
 */
func getEncoder(conf LogConfigs) zapcore.Encoder {
	encoderConfig := zapcore.EncoderConfig{
		NameKey:        "logger",
		FunctionKey:    zapcore.OmitKey,
		MessageKey:     "msg",
		StacktraceKey:  "stacktrace",
		LineEnding:     zapcore.DefaultLineEnding,
		EncodeLevel:    zapcore.LowercaseLevelEncoder,
		EncodeTime:     zapcore.EpochTimeEncoder,
		EncodeDuration: zapcore.SecondsDurationEncoder,
	}
	encoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout(TIME_LAYOUT) // log 时间格式 例如: 2021-09-11t20:05:54.852+0800
	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder             // 输出level序列化为全大写字符串，如 INFO DEBUG ERROR
	//encoderConfig.EncodeCaller = zapcore.FullCallerEncoder
	//encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
	if conf.LogFormat == "json" {
		return zapcore.NewJSONEncoder(encoderConfig) // 以json格式写入
	}
	return zapcore.NewConsoleEncoder(encoderConfig) // 以logfmt格式写入
}

/**
 * @brief: 	获取日志输出方式  日志文件 控制台
 * @author: lianpu.sun
 */
func getLogWriter(conf LogConfigs) (zapcore.WriteSyncer, error) {
	// 判断日志路径是否存在，如果不存在就创建
	if exist := isExist(conf.LogPath); !exist {
		if conf.LogPath == "" {
			conf.LogPath = DefaultLogPath
		}
		if err := os.MkdirAll(conf.LogPath, os.ModePerm); err != nil {
			conf.LogPath = DefaultLogPath
			if err := os.MkdirAll(conf.LogPath, os.ModePerm); err != nil {
				return nil, err
			}
		}
	}

	hook, _ := rotatelogs.New(
		conf.LogPath+"%Y%m%d_"+conf.LogFileName,
		rotatelogs.WithRotationCount(conf.LogFileMaxBackups),
		rotatelogs.WithMaxAge(conf.LogMaxAge),
		rotatelogs.WithRotationTime(conf.LogSplitTime),
	)
	if conf.LogStdout {
		// 日志同时输出到控制台和日志文件中
		return zapcore.NewMultiWriteSyncer(zapcore.AddSync(hook), zapcore.AddSync(os.Stdout)), nil
	} else {
		// 日志只输出到日志文件
		return zapcore.AddSync(hook), nil
	}
}

/**
 * @brief: 	判断文件或者目录是否存在
 * @author: lianpu.sun
 */
func isExist(path string) bool {
	_, err := os.Stat(path)
	return err == nil || os.IsExist(err)
}

func Info(format string, v ...interface{}) {
	sugaredLogger.Infof(time.Now().Format(TIME_LAYOUT)+"[INFO] :"+format, v...)
}

func Error(format string, v ...interface{}) {
	sugaredLogger.Errorf(time.Now().Format(TIME_LAYOUT)+"[ERROR]:"+format, v...)
}

func Debug(format string, v ...interface{}) {
	sugaredLogger.Debugf(time.Now().Format(TIME_LAYOUT)+"[DEBUG]:"+format, v...)
}

func Warn(format string, v ...interface{}) {
	sugaredLogger.Warnf(time.Now().Format(TIME_LAYOUT)+"[WARN] :"+format, v...)
}

func Panic(format string, v ...interface{}) {
	sugaredLogger.Panicf(time.Now().Format(TIME_LAYOUT)+"[PANIC]:"+format, v...)
}
